using UnityEngine;
using System.Collections;


//serian los personajes que manejas
public class Character : Actor
{

#region Constants
    /// <summary>
    /// Cuanto estamina te baja por segundo al correr
    /// </summary>
    const float c_StaminaDrain = 20;
    /// <summary>
    /// Cuanto estamina regenera por segundo al estar quieto.
    /// </summary>
    const float c_StaminaRegen = 5;
    const float c_PickItemDistance = 3;
    const float c_DragDistance = 3;
    public const int c_CharacterLayer = 8;
    /// <summary>
    /// por cuanto se multiplica el sonido de movimiento si corres
    /// </summary>
    const float c_SprintingNoiseFactor = 2.0f;
    /// <summary>
    /// Cada cuanto tiempo haces ruido movimiendote.
    /// </summary>
    const float c_MovingSoundTime = 0.5f;


    #endregion

#region Members
    public int m_CharacterIndex = -1;

    /// <summary>
    /// Multiplicador de velocidad al ir agachado.
    /// </summary>
    public float m_CrouchingSpeedFactor = 0.5f;
    /// <summary>
    /// Multiplicador de velocidad al ir corriendo.
    /// </summary>
    float m_SprintingSpeedFactor = 2.0f;

    Item m_ItemToPick = null;

    [HideInInspector]
    public CharacterData m_CharacterData;
    float soundTimer = 0.0f;


    bool m_CanDragCorpses = false;
    Enemy m_EnemyToDrag = null;
    Enemy m_DraggedEnemy = null;
#endregion

    public override void Start()
    {
        base.Start();
        Player.instance.AddCharacter(this, m_CharacterIndex);

        /*m_AttributeSet = new AttributeSet();
        m_PerkSet = new Perks.PerkSet(m_AttributeSet);
        m_Inventory = new Inventory();*/


        CharacterData cd = CharacterDataManager.GetInstance().GetCharacterData(m_CharacterIndex);
        if (cd.assigned == false)
        {
            cd.assigned = true;
            m_CharacterData = cd;
            m_CharacterData.CreateInventory();
        }
        else
        {
            Debug.LogError("Hay 2 characters con el mismo character index");
        }
        /*if(m_CharacterIndex == -1)
        {
            m_CharacterData = new CharacterData();
        }
        else
        {
            m_CharacterData = CharacterDataManager.GetInstance().GetCharacterData(m_CharacterIndex);
        }*/
        CreateInitialWeapons();

        if (m_CharacterData.perks.IsEquipped(Perks.TransportBodies.Name))
        {
            m_CanDragCorpses = true;
        }
        if(m_Animator != null)
        m_Animator.Initialize(this, m_CharacterIndex);

    }
    public override void Update()
    {
        base.Update();
        if (!m_Moving)
        {
            if (m_ItemToPick != null)
            {
                if (Vector3.Distance(this.transform.position, m_ItemToPick.transform.position) < c_PickItemDistance)
                {
                    StoreItem(m_ItemToPick);
                    m_ItemToPick = null;
                }
                else //si el item a agarrar no es null,te dejaste de mover y no estas alado significa que se cancelo la orden. 
                {
                    m_ItemToPick = null;
                }
             }
            if (m_EnemyToDrag != null)
            {
                if (Vector3.Distance(this.transform.position, m_EnemyToDrag.transform.position) < c_DragDistance)
                {
                    m_DraggedEnemy = m_EnemyToDrag;
                    m_EnemyToDrag = null;
                }
                else
                {
                    m_EnemyToDrag = null;
                }
            }

            //regenero stamina si estoy quieto.
            RegenerateStamina();
        }
        else //si me estoy moviendo
        {
            OnMove();
            if (m_Crouching)
            {
                RegenerateStamina();
            }
        }

        if (m_DraggedEnemy != null)
        {
            m_DraggedEnemy.transform.position = this.transform.position;
        }
    }

    public void Crouch()
    {
        StopMoving();
        m_Crouching = !m_Crouching;
    }
    public void OnMove()
    {
        if (m_Sprinting)
        {
            ConsumeStamina();
        }
        if(m_Crouching==false)
        {
            soundTimer += Time.deltaTime;
            if (soundTimer >= c_MovingSoundTime)
            {
                soundTimer -= c_MovingSoundTime;
                if (m_Sprinting == false)
                {
                    if(m_CharacterData.perks.IsEquipped(Perks.SilentAssassin.Name) == false)
                    VisionManager.GetInstance().GenerateSound(this.transform.position, m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.NOISE_FACTOR));
                }
                else // si corre hace mas ruido, modificado por el SprintingNoiseFactor
                {
                    VisionManager.GetInstance().GenerateSound(this.transform.position, m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.NOISE_FACTOR) * c_SprintingNoiseFactor);
                }
            }
        }
    }
    public void Sprint()
    {
        if (!m_Crouching)
        {
            m_Sprinting = true;
        }
    }
    /// <summary>
    /// Se llama cuando ordenas al personaje a moverse.
    /// </summary>
    public override void OnMoveTo()
    {
        if (m_Equipable != null)
        {
            if (m_Equipable is KnifeBehaviour)
            {
                ((KnifeBehaviour)m_Equipable).CancelOrder();
            }
        }
        m_EnemyToDrag = null;
    }
    private void RegenerateStamina()
    {
        if (m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.STAMINA)
            < m_CharacterData.attributes.GetSubAttributeMaxValue(SUB_ATTRIBUTES.STAMINA))
        {
            m_CharacterData.attributes.ModifySubAttributeActualValue(SUB_ATTRIBUTES.STAMINA, c_StaminaRegen * Time.deltaTime);
        }
    }
    private void ConsumeStamina()
    {
        m_CharacterData.attributes.ModifySubAttributeActualValue(SUB_ATTRIBUTES.STAMINA, -c_StaminaDrain * Time.deltaTime);
        if (m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.STAMINA) == 0)
        {
            m_Sprinting = false;
        }
    }
    /// <summary>
    /// Orden de agarrar cierto item. Si esta cerca lo guarda de una en el inventario, si no lo va a buscar.
    /// </summary>
    /// <param name="item"></param>
    public void PickUpItem(Item item)
    {
        if (Vector3.Distance(this.transform.position, item.transform.position) < c_PickItemDistance)
        {
            StoreItem(item);
        }
        else
        {
            MoveToItem(item);
        }
    }
    /// <summary>
    /// guarda el item en el inventario
    /// </summary>
    /// <param name="item"></param>
    private void StoreItem(Item item)
    {
        m_CharacterData.inventory.StockItem(item);
    }
    /// <summary>
    /// Hace que el personaje intente moverse hacia el item y agarrarlo.
    /// </summary>
    /// <param name="item"></param>
    private void MoveToItem(Item item)
    {
        //El moveTo devuelve si se puede mover hasta ahi.
        if (MoveTo(item.transform.position))
        {
            m_ItemToPick = item;
        }
    }
    public void ReloadWeapon(string weaponId, Bullets bullets)
    {
        if (m_Equipable is WeaponBehaviour)
        {
            WeaponBehaviour w = m_Equipable as WeaponBehaviour;
            if (w.m_Template.id == weaponId)
            {
                w.Reload(bullets);
            }
        }
    }
    /// <summary>
    /// Se llama cuando el jugador selecciona a un personaje.
    /// </summary>
    public void OnSelect()
    {
        InventoryController.GetInstance().SetActiveInventory(this.m_CharacterData.inventory);
        AttributesController.GetInstance().SetActiveCharacter(this.m_CharacterData.attributes);
		CharacterDataManager.GetInstance().SetCurrentIndex(this.m_CharacterIndex);
		InventoryController.GetInstance().GetGUIPanel().DrawHotSkills();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns>Si no estas agachado usa la velocidad del set de atributos
    /// Si estas agachado multiplica el valor por el factor de velocidad cuando estas agachado</returns>
    protected override float GetSpeed()
    {
        if (m_Crouching == true)
            return m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.MOVEMENT_SPEED) * m_CrouchingSpeedFactor * m_SpeedFactor;
        else if (m_Sprinting == true)
            return m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.MOVEMENT_SPEED) * m_SpeedFactor * m_SprintingSpeedFactor;
        else
            return m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.MOVEMENT_SPEED) * m_SpeedFactor;
    }
    public override void OnHit(float damage, Actor damager)
    {
        //Debug.Log("Le pegaron a un personaje");
        if (m_Alive == false) return;
        GameObject go = PrefabManager.GetInstance().CreateFromPool("bloodsplat");
        if (go != null)
        {
            go.transform.position = this.transform.position + Vector3.up * 5;
        }
        SoundManager.Instance.PlaySound("Scream",true);


        GUIPanelInGame.instance.ShowRedFlashForPortrait(m_CharacterIndex);
        m_CharacterData.attributes.ModifySubAttributeActualValue(SUB_ATTRIBUTES.HIT_POINTS, -damage);
        if (m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.HIT_POINTS) <= 0)
        {
            OnDeath();
        }
    }
    public override void OnDeath()
    {
        m_Alive = false;
        this.gameObject.layer = 10;
        m_Animator.PlayDeadAnimation();
        Player.instance.UnSelectCharacter(this);
        Player.instance.OnCharacterDeath();
		StopMoving();
    }
    /// <summary>
    /// Cura al personaje
    /// </summary>
    /// <param name="amount"></param>
    /// <returns>Devuelve si lo pudo curar, si esta full hp no lo puede curar por ejemplo</returns>
    public bool HealCharacter(int amount)
    {
        if (m_CharacterData.attributes.GetSubAttributeActualValue(SUB_ATTRIBUTES.HIT_POINTS)
            < m_CharacterData.attributes.GetSubAttributeMaxValue(SUB_ATTRIBUTES.HIT_POINTS))
        {
            m_CharacterData.attributes.ModifySubAttributeActualValue(SUB_ATTRIBUTES.HIT_POINTS, amount);
            return true;
        }
        else
        {
            return false;
        }
    }
    void CreateInitialWeapons()
    {
        Player.instance.SelectCharacter(this);

        //pistola sin importar si tenes perk
        Weapon prefab = PrefabManager.GetInstance().GetPrefab("weaponitem").GetComponent<Weapon>();
        prefab.m_WeaponId = "pistol";
        Weapon w =  ((GameObject)GameObject.Instantiate(prefab.gameObject)).GetComponent<Weapon>();
        w.Initialize();
        m_CharacterData.inventory.StockItem(w);
        w.Use();

        //Rifle si tenes el perk
        if (m_CharacterData.perks.IsEquipped(Perks.RifleProficiency.Name))
        {
            prefab.m_WeaponId = "rifle";
            w = ((GameObject)GameObject.Instantiate(prefab.gameObject)).GetComponent<Weapon>();
            w.Initialize();
            m_CharacterData.inventory.StockItem(w);
        }
        //Sniper si tenes el perk
        if (m_CharacterData.perks.IsEquipped(Perks.SniperProficiency.Name))
        {
            prefab.m_WeaponId = "sniper";
            w = ((GameObject)GameObject.Instantiate(prefab.gameObject)).GetComponent<Weapon>();
            w.Initialize();
            m_CharacterData.inventory.StockItem(w);
        }

        //cuchillo no requiere perk
        KnifeItem knife = ((GameObject)GameObject.Instantiate(PrefabManager.GetInstance().GetPrefab("knifeitem"))).GetComponent<KnifeItem>();
        knife.Initialize();
        m_CharacterData.inventory.StockItem(knife);
    }
    public bool CanDragCorpses()
    {
        return (m_CanDragCorpses && m_DraggedEnemy == null);
    }
    public void DragCorpse(Enemy enemy)
    {
        if (Vector3.Distance(this.transform.position, enemy.transform.position) < c_DragDistance)
        {
            m_DraggedEnemy = enemy;
        }
        else
        {
            if (MoveTo(enemy.transform.position))
            {
                m_EnemyToDrag = enemy;
            }
        }
    }
    public void DropCorpse()
    {
        if (m_DraggedEnemy == null) return;
        m_DraggedEnemy = null;
    }
    public void UseHotSkill(int index)
    {
        m_CharacterData.UseHotSkill(index);
    }
}
